#include "CargarDatos.h"

using namespace std;

int METODO;
int HEURISTICA;
string metodo;
int GIVENS = 1;
int HOUSEHOLDER = 2;

int HEURISTICA_1 = 1;
int HEURISTICA_2 = 2;
int HEURISTICA_3 = 3;
int HEURISTICA_4 = 4;
int HEURISTICA_5 = 5;

int cant=0;
double error= 0.00001;
vector<double> autovalores;
vector<int> livianosOrig;
vector<int> pesadosOrig;
clock_t init, final;
char* coso;


int cargarParam(FILE* datos)
{
    char buffer[1024]=" ";
    char temp = getc(datos);
    int i = 0;
    while(!(temp == ' ' || temp == '\n'))
    {
        buffer[i] = temp;
        temp = getc(datos);
        i++;
    }
    return atoi(buffer);
}


Matriz crearMatrizK(int n,int* coeficientes)
{
    Matriz K(n,n);

    K.set(1,1,(double)(- coeficientes[1] - coeficientes[2]));
    K.set(1,2,(double)(coeficientes[2]));

    for(int i=2; i<n; i++)
    {
        K.set(i,i, (double)(-coeficientes[i] - coeficientes[i+1]));
        K.set(i,i+1, (double)(coeficientes[i+1]));
        K.set(i,i-1, (double)(coeficientes[i]));
    }

    K.set(n,n,(double)(-coeficientes[n]));
    K.set(n,n-1,(double)(coeficientes[n]));

    return K;
}


Matriz crearMatrizM(int n, int m0, int ml, int mp, int* livianos, int* pesados)
{
    Matriz M(n,n);

    for(int i=1; i<=n; i++)     M.set(i,i, (double)( m0 + livianos[i]*ml + pesados[i]*mp));

    return M;
}

Matriz crearMatrizA(int n, int m0, int ml, int mp, int* livianos, int* pesados,int* coeficientes)
{
    Matriz K(n,n);

    K.set(1,1,(double)(- coeficientes[1] - coeficientes[2])/(double)( m0 + livianos[1]*ml + pesados[1]*mp));
    K.set(1,2,(double)(coeficientes[2]/(double)( m0 + livianos[1]*ml + pesados[1]*mp)));

    for(int i=2; i<n; i++)
    {
        K.set(i,i, (double)(-coeficientes[i] - coeficientes[i+1])/(double)( m0 + livianos[i]*ml + pesados[i]*mp));
        K.set(i,i+1, (double)(coeficientes[i+1])/(double)( m0 + livianos[i]*ml + pesados[i]*mp));
        K.set(i,i-1, (double)(coeficientes[i])/(double)( m0 + livianos[i]*ml + pesados[i]*mp));
    }

    K.set(n,n,(double)(-coeficientes[n])/(double)( m0 + livianos[n]*ml + pesados[n]*mp));
    K.set(n,n-1,(double)(coeficientes[n])/(double)( m0 + livianos[n]*ml + pesados[n]*mp));

    return K;
}


void haceLaMagia(int n,int m0,int ml,int mp,int* pesados,int* livianos,int* coeficientes)
{
    bool pisosSeguros[n+1];
    bool hayChances;
    ofstream pepe;
    pepe.open("./RESULTADOS.txt",ios::app);
int cantAutosL= 0;
        int cantAutosP= 0;
    do
    {
        cout<<"Creando la Matriz A = M-1 * K"<<endl;
        Matriz A = crearMatrizA(n,m0,ml,mp, livianos, pesados,coeficientes);
        cout<<"Arrancando Algoritmo QR"<<endl;
        int i =0;

        if (METODO == GIVENS)
        {
            while(!A.triSup())
            {
                i++;
                Matriz R = A;
                Matriz Q = calcularQRRotaciones(R);

                A = R.multiplicar(Q);
                A.redondear();
            }
            cout<<endl<< "Se realizaron "<<i<<" iteraciones"<<endl<<endl;

        }
        else if (METODO == HOUSEHOLDER)
        {

            while(!A.triSup())
            {
                i++;
                Matriz R = A;
                Matriz Q = calcularQRReflexiones(R);

                A = R.multiplicar(Q);
                A.redondear();
            }
            cout<<endl<< "Se realizaron "<<i<<" iteraciones"<<endl<<endl;
        }


        cout<<endl<<"Los Coeficientes de rigides: "<<endl;
        autovalores.resize(n+1);
        ///Imprimir Autovalores
        for(int i=1; i<=A.size(); i++)
        {
            if(A.get(i,i)>0)
            {
                cout<<endl<<endl<<"ERROR!!!! El AUTOVALOR "<<A.get(i,i)<<" DEBERIA SER NEGATIVO!!"<<endl<<endl;
                exit(-1);
            }
            autovalores[i]= sqrt(-A.get(i,i));
            cout<<autovalores[i]<<endl;
        }

        ///Imprimir Autos livianos y pesados
        cout<<endl;
        estaSeguro(n, autovalores,pisosSeguros);
        cout<<"Pisos Seguros: "<< endl;
        for(int i=1; i<=n; i++) cout<<setw(2)<<pisosSeguros[i]<<" ";
        cout<<endl;

        cout<<"Cant de movimientos: "<<cant++<<endl;
        if(cant > 1000)
        {
            cout<<endl<<"CON LA HEURISTICA "<<HEURISTICA<<" NO HAY SOLUCION!! Se excedio el limite de iteraciones"<<endl<<endl;
            final=clock()-init;
            pepe<<"Archivo: "<<coso<<"\t Autos movidos: "<<0<<"\t tiempo: "<<(double)final / ((double)CLOCKS_PER_SEC)<<"\t Metodo: "<<metodo<<"\t Heuristica: "<<HEURISTICA<<endl;
            return;
        }

        hayChances = correrHeuristica(n,livianos,pesados,pisosSeguros);                                               ///HEURISTICA
        cout<<"Livianos: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(2)<<livianos[i]<<" ";
        cout<<endl<<endl;
        cout<<"Pesados: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(2)<<pesados[i]<<" ";
        cout<<endl;
    }
    while(!estaSeguro(n, autovalores,pisosSeguros) && hayChances);
    if(estaSeguro(n, autovalores,pisosSeguros))
    {
        cout<<endl<<endl<<"EL EDIFICIO ESTA SEGURO!"<<endl;
        cout<<"Livianos: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(4)<<livianos[i]<<" ";
        cout<<endl<<endl;
        cout<<"Pesados: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(4)<<pesados[i]<<" ";
        cout<<endl;



        for(int i =1 ; i<=n; i++)
        {
            if((livianos[i]-livianosOrig[i]) > 0)
            {
                cantAutosL+=(livianos[i]-livianosOrig[i]);
            }
        }
        for(int i =1 ; i<=n; i++)
        {
            if((pesados[i]-pesadosOrig[i]) > 0)
            {
                cantAutosP+=(pesados[i]-pesadosOrig[i]);
            }
        }
        cout<<endl<<"Cant de movimientos:            "<<cant<<endl<<endl;
        cout<<"Cant de autos livianos movidos: "<<cantAutosL<<endl<<endl;
        cout<<"Cant de autos pesados movidos:  "<<cantAutosP<<endl<<endl;
        cout<<"total:                          "<<cantAutosL+cantAutosP<<endl<<endl;
        cout<<"tiempo:                         "<<(double)final / ((double)CLOCKS_PER_SEC)<<endl<<endl;
    }
    else
    {
        cout<<endl<<"CON LA HEURISTICA "<<HEURISTICA<<" NO HAY SOLUCION!!"<<endl<<endl;
        cout<<"Livianos: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(4)<<livianos[i]<<" ";
        cout<<endl<<endl;
        cout<<"Pesados: "<<endl;
        for(int i =1 ; i<=n; i++)cout<<setw(4)<<pesados[i]<<" ";
        cout<<endl;
    }
    final=clock()-init;
    pepe<<"Archivo: "<<coso<<"\t Autos movidos: "<<cantAutosL+cantAutosP<<"\t tiempo: "<<(double)final / ((double)CLOCKS_PER_SEC)<<"\t Metodo: "<<metodo<<"\t Heuristica: "<<HEURISTICA<<endl;
}

bool estaSeguro(int n, vector<double> autovalores, bool* pisosSeguros)
{
    bool res = true;
    for(int i=1; i<=n; i++)
    {
        pisosSeguros[i] = fabs(autovalores[i]-3) > 0.3;
        res = res && pisosSeguros[i];
    }
    //cout<<"Edificio seguro?: "<< res<<endl;
    return res;
}

bool reacomodar1(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Desalojo 1L y 1P a cada otro piso que este seguro
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            for(int j=1; j<=n; j++)
            {
                if(pisosSeguros[j] && pesados[i]>0 && livianos[i]>0)
                {
                    livianos[i]--;
                    livianos[j]++;
                    pesados[i]--;
                    pesados[j]++;
                    return true;
                }
                if(pisosSeguros[j] && pesados[i]>0)
                {
                    pesados[i]--;
                    pesados[j]++;
                    return true;
                }
                if(pisosSeguros[j] && livianos[i]>0)
                {
                    livianos[i]--;
                    livianos[j]++;
                    return true;
                }
            }
        }
    }
    return false;
}

bool reacomodar2(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Desalojo primero los pesados y despues los livianos
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            for(int j=1; j<=n; j++)
            {
                if(pisosSeguros[j] && (pesados[i]>0 || livianos[i]>0))
                {
                    if(pesados[i]==0)
                    {
                        livianos[i]--;
                        livianos[j]++;
                    }
                    else
                    {
                        pesados[i]--;
                        pesados[j]++;
                    }
                    return true;
                }
            }
        }
    }
    return false;
}

bool reacomodar3(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Agrego al piso primero los pesados y despues los livianos
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            for(int j=1; j<=n; j++)
            {
                if(pisosSeguros[j] && (pesados[j]>0 || livianos[j]>0))
                {

                    if(pesados[j]==0)
                    {
                        livianos[j]--;
                        livianos[i]++;
                    }
                    else
                    {
                        pesados[j]--;
                        pesados[i]++;
                    }
                    return true;
                }
            }
        }
    }
    return false;
}

bool reacomodar4(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Acomodo segun los coeficientes de rigidez, si es
/// menor a 3 le saco peso, si es >3 le agrego
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            if(autovalores[i]< 3 && (livianos[i]>0 || pesados[i]>0))
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3 && autovalores[j]<3.3)
                    {
                        if(livianos[i]>0)
                        {
                            livianos[i]--;
                            livianos[j]++;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return true;
                    }

                    if(autovalores[j]>3 && i!=j)
                    {
                        if(livianos[i]>0)
                        {
                            livianos[j]++;
                            livianos[i]--;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return true;
                    }

                    if(autovalores[j]<2.3 && i!=j)
                    {
                        if(livianos[i]>0)
                        {
                            livianos[j]++;
                            livianos[i]--;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return true;
                    }
                }
                return false;
            }
            else
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]<3 && autovalores[j]>2.7 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[j]--;
                            livianos[i]++;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[j]--;
                            pesados[i]++;
                        }
                        return true;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]<3 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return true;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3.3 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return true;
                    }
                }
                return false;
            }
        }
    }
    return false;
}


bool reacomodar5(int n, int* livianos,int* pesados,bool* pisosSeguros) ///Acomodo segun los coeficientes de rigidez, si es
/// menor a 3 le agrego peso, si es > 3 le saco
{
    for(int i=1; i<=n; i++)
    {
        if(!pisosSeguros[i])
        {
            if(autovalores[i]< 3)
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3 && autovalores[j]<3.3 && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0 && i!=j)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return true;
                    }

                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return true;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]<2.3 && i!=j && (livianos[j]>0 || pesados[j]>0))
                    {
                        if(livianos[j]>0)
                        {
                            livianos[i]++;
                            livianos[j]--;
                        }
                        if(pesados[j]>0)
                        {
                            pesados[i]++;
                            pesados[j]--;
                        }
                        return true;
                    }
                }
                return false;
            }
            else
            {
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]<3 && autovalores[j]>2.7 && i!=j&& (livianos[i]>0 || pesados[i]>0))
                    {
                        if(livianos[i]>0)
                        {
                            livianos[i]--;
                            livianos[j]++;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[i]--;
                            pesados[j]++;
                        }
                        return true;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]<3 && i!=j&& (livianos[i]>0 || pesados[i]>0))
                    {
                        if(livianos[i]>0)
                        {
                            livianos[j]++;
                            livianos[i]--;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return true;
                    }
                }
                for(int j=1; j<=n; j++)
                {
                    if(autovalores[j]>3.3 && i!=j&& (livianos[i]>0 || pesados[i]>0))
                    {
                        if(livianos[i]>0)
                        {
                            livianos[j]++;
                            livianos[i]--;
                        }
                        if(pesados[i]>0)
                        {
                            pesados[j]++;
                            pesados[i]--;
                        }
                        return true;
                    }
                }


                return false;
            }
        }
    }
    return false;
}

void haceTodaLaMagia(char* archivo)
{
    FILE* datos = fopen(archivo, "r");
    coso = archivo;
    int n = cargarParam(datos);
    int m0 = cargarParam(datos);
    int ml = cargarParam(datos);
    int mp = cargarParam(datos);
    int pesados[n+1], livianos[n+1], coeficientes[n+1];
    livianosOrig.resize(n+1);
    pesadosOrig.resize(n+1);

    for(int i=1; i<=n; i++)        coeficientes[i] = cargarParam(datos);
    for(int i=1; i<=n; i++)
    {
        livianos[i] = cargarParam(datos);
        livianosOrig[i]=livianos[i];
    }
    for(int i=1; i<=n; i++)
    {
        pesados[i] = cargarParam(datos);
        pesadosOrig[i]=pesados[i];
    }

    haceLaMagia(n, m0, ml, mp, pesados, livianos, coeficientes);

}

void preparoHechizo(char* edificio, int met, int heur)
{
    init=clock();
    /// Seteando metodo
    if (met == GIVENS)
    {
        METODO = GIVENS;
        metodo ="Givens";
    }
    else if ( met == HOUSEHOLDER)
    {
        METODO = HOUSEHOLDER;
        metodo = "Householder";
    }
    else
    {
        cout<<"Le chingaste al metodo titan!"<<endl;
        exit(-1);
    }

    /// Seteando heuristica
    if (heur == HEURISTICA_1)
    {
        HEURISTICA = HEURISTICA_1;
    }
    else if (heur == HEURISTICA_2)
    {
        HEURISTICA = HEURISTICA_2;
    }
    else if (heur == HEURISTICA_3)
    {
        HEURISTICA = HEURISTICA_3;
    }
    else if (heur == HEURISTICA_4)
    {
        HEURISTICA = HEURISTICA_4;
    }
    else if (heur == HEURISTICA_5)
    {
        HEURISTICA = HEURISTICA_5;
    }
    else
    {
        cout<<"Le chingaste a la heuristica titan!"<<endl;
        exit(-1);
    }

    haceTodaLaMagia(edificio);
}

bool correrHeuristica(int n, int* livianos,int* pesados,bool* pisosSeguros)
{

    if (HEURISTICA == HEURISTICA_1)
    {
        return  reacomodar1(n,livianos,pesados,pisosSeguros);
    }
    else if (HEURISTICA == HEURISTICA_2)
    {
        return reacomodar2(n,livianos,pesados,pisosSeguros);
    }
    else if (HEURISTICA == HEURISTICA_3)
    {
        return reacomodar3(n,livianos,pesados,pisosSeguros);
    }
    else if (HEURISTICA == HEURISTICA_4)
    {
        return reacomodar4(n,livianos,pesados,pisosSeguros);
    }
    else if (HEURISTICA == HEURISTICA_5)
    {
        return reacomodar5(n,livianos,pesados,pisosSeguros);
    }
    return false;
}
